#include <head.h>
#include <iostream>
#include <functional>

#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
#include <nlohmann/json.hpp>

using std::cout;
using std::endl;
using std::string;


class MuploadServer {
public:
    MuploadServer(unsigned short port)
    : _port(port)
    //, _httpServer(std::bind(&MuploadServer::process, this, std::placeholders::_1))
    , _httpServer([this](WFHttpTask * serverTask){  this->process(serverTask); })
    , _waitGroup(1)
    {}

    void start();
    void stop();

private:
    void process(WFHttpTask * serverTask);
    void init(WFHttpTask * serverTask, const string & username);
    void uppart(WFHttpTask * serverTask, const string & uploadID, const string & chunkidx);
    void complete(WFHttpTask * serverTask, const string & uploadID);

    string produceUploadID(const string & username);

private:
    unsigned short _port;
    WFHttpServer _httpServer;
    WFFacilities::WaitGroup _waitGroup;
};

void MuploadServer::start()
{
    if(_httpServer.start(_port) == 0) {
        _waitGroup.wait();
        stop();
    } else {
        printf("Http Server start failed.\n");
    }
}

void MuploadServer::stop()
{
    _httpServer.stop();
}

void MuploadServer::process(WFHttpTask * serverTask)
{
    printf("\n\nMuploadServer::process is running.\n");
    //0. 对任务的状态进行检测
    //1. 解析请求
    auto req = serverTask->get_req();
    string method = req->get_method();
    //uri中一定是有查询词的
    string uri = req->get_request_uri();
    string path = uri.substr(0, uri.find("?"));
    cout << "method: " << method << endl;
    cout << "path: " << path << endl;

    if(method == "POST" && path == "/file/mupload/init") {
        // uri:  /file/mupload/init?username=lwh
        //解析username
        string usernameKV = uri.substr(uri.find("?") + 1);
        string username = usernameKV.substr(usernameKV.find("=") + 1);
        cout << "init section: username=> " << username << endl;
        init(serverTask, username);
    } else if(method == "POST" && path == "/file/mupload/uppart") {
        // uri: /file/mupload/uppart?uploadid=lwhxxx&idx=0
        //解析uploadID和chunkidx
        string uploadIDKV = uri.substr(0, uri.find("&"));
        string chunkidxKV = uri.substr(uri.find("&") + 1);
        string uploadID = uploadIDKV.substr(uploadIDKV.find("=") + 1);
        string chunkidx = chunkidxKV.substr(chunkidxKV.find("=") + 1);
        cout << "uppart section: id=> " << uploadID << ", idx=> " << chunkidx << endl;
        uppart(serverTask, uploadID, chunkidx);
    } else if(method == "GET" && path == "/file/mupload/complete") {
        // uri:  /file/mupload/complete?uploadid=lwhxxx
        //解析uploadID
        string uploadID = uri.substr(uri.find("=") + 1);
        cout << "complete section: uploadID=> "  << uploadID << endl;
        complete(serverTask, uploadID);
    }
}

//注意：每一个函数的行数尽量不要超过50行,可读性更强
void MuploadServer::init(WFHttpTask * serverTask, const string & username)
{
    cout << "init is running.\n";
    //1. 生成uploadID
    string uploadID = produceUploadID(username);
    cout << "uploadID: " << uploadID << endl;
    //2. 生成分片信息
    size_t chunksize = 1024 * 1024 * 100;
    //3. 获取请求报文的消息体来拿到文件相关信息
    auto req = serverTask->get_req();
    //3.1获取消息体
    const void * body = nullptr;
    size_t size = 0;
    req->get_parsed_body(&body, &size);
    string fileInfoStr((const char *)body, size);
    //3.2将消息体转换为一个JSON对象
    using Json = nlohmann::json;
    Json fileInfo = Json::parse(fileInfoStr);//反序列化
    size_t filesize = fileInfo["filesize"];
    printf("filesize: %ld\n", filesize);
    size_t chunkcnt = filesize / chunksize + (filesize % chunksize > 0 ? 1 : 0);
    printf("chunkcnt: %ld\n", chunkcnt);
    fileInfo["chunksize"] = chunksize;
    fileInfo["chunkcnt"] = chunkcnt;
    fileInfo["uploadID"] = uploadID;
    fileInfoStr = fileInfo.dump();//序列化
    cout << "serilize info: " << fileInfoStr << endl;
    //3.3生成响应信息
    auto resp = serverTask->get_resp();
    resp->append_output_body(fileInfoStr);

    //4. 写入Redis中
    string url("redis://localhost:6379");
    auto redisTask = WFTaskFactory::create_redis_task(url, 1, nullptr);
    string command = "HSET";
    std::vector<string> params{
        uploadID,
        "filename", fileInfo["filename"],
        "filesize", std::to_string(filesize),
        "filehash", fileInfo["filehash"],
        "chunksize", std::to_string(chunksize),
        "chunkcnt", std::to_string(chunkcnt)
    };
    redisTask->get_req()->set_request(command, params);
    series_of(serverTask)->push_back(redisTask);
}

void MuploadServer::uppart(WFHttpTask * serverTask, const string & uploadID, const string & chunkidx)
{
    cout << "uppart is running.\n";
    auto resp = serverTask->get_resp();
    //1.获取请求的消息体, 消息体就是分片的数据
    const void * body = nullptr;
    size_t size = 0;
    serverTask->get_req()->get_parsed_body(&body, &size);
    printf("body size: %ld\n", size);
    //2.将uploadID作为临时文件夹, 写入分片信息
    mkdir(uploadID.c_str(), 0755);
    string filepath = uploadID + "/" + chunkidx;
    int fd = open(filepath.c_str(), O_CREAT|O_RDWR, 0644);
    if(fd < 0) {
        perror("open");
        //告诉对端分片上传失败
        resp->append_output_body("upload uppart failed!");
        return;
    }
    int ret = write(fd, body, size);
    printf("write ret: %d\n", ret);
    //3. 生成响应信息
    resp->append_output_body("upload uppart success!");
    //4. 将分片信息的记录写入Redis
    string url("redis://localhost:6379");
    auto redisTask = WFTaskFactory::create_redis_task(url, 1, nullptr);
    string command("HSET");
    std::vector<string> params{
        uploadID,
        "chunkidx_" + chunkidx, "1" 
    };
    redisTask->get_req()->set_request(command, params);
    series_of(serverTask)->push_back(redisTask);
}

void MuploadServer::complete(WFHttpTask * serverTask, const string & uploadID)
{
    cout << "complete is running.\n";
    string url("redis://localhost:6379");
    auto redisTask = WFTaskFactory::create_redis_task(url, 1, 
    [serverTask](WFRedisTask * redistask){
        //0. 检测任务的状态
        //1. 获取Redis任务的响应数据
        protocol::RedisValue result;
        redistask->get_resp()->get_result(result);

        int chunkcnt = 0;
        int chunkcurrent = 0;
        if(result.is_array()) {
            for(size_t i = 0; i < result.arr_size(); i += 2) {
                string key = result.arr_at(i).string_value();
                string value = result.arr_at(i + 1).string_value();
                if(key == "chunkcnt") {
                    chunkcnt = atoi(value.c_str());
                    printf("> chunkcnt: %d\n", chunkcnt);
                }
                if(key.substr(0, 9) == "chunkidx_") {
                    ++chunkcurrent;
                }
            }
            printf("> chunkcurrent: %d\n", chunkcurrent);

            //整个for执行完毕
            if(chunkcnt != chunkcurrent) {
                //文件上传失败的情况
               serverTask->get_resp()->append_output_body("upload file operation failed.");
            } else {
                //分片数量相等的情况
                //... 合并文件
                //... 验证filehash
                //
                //文件上传成功
               serverTask->get_resp()->append_output_body("upload file operation success.");
            }
        } else {
            //文件上传失败的情况
            serverTask->get_resp()->append_output_body("upload file operation failed.");
        }
    });
    string command("HGETALL");
    std::vector<string> params{uploadID};
    redisTask->get_req()->set_request(command, params);
    series_of(serverTask)->push_back(redisTask);
}

string MuploadServer::produceUploadID(const string & username)
{
    time_t tm = time(nullptr);
    struct tm * ptm = localtime(&tm);
    char buff[15] = {0};
    sprintf(buff, "%04d%02d%02d%02d%02d%02d",
                ptm->tm_year + 1900,
                ptm->tm_mon + 1,
                ptm->tm_mday,
                ptm->tm_hour,
                ptm->tm_min,
                ptm->tm_sec);
    return username + buff;
}

void test0()
{
    MuploadServer muploadServer(8888);
    muploadServer.start();

    cout << "test0 exit" << endl;
}


int main()
{
    test0();
    return 0;
}

